home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 June: Reference Library / Dev.CD Jun 96 RL / Dev.CD Jun 96 RL.toast / Technical Documentation / develop / develop Issue 24 / develop Issue 24 code / Scriptable Database 1.0a15 / Scripting / Accessor.cp next >
Encoding:
Text File  |  1996-02-19  |  18.7 KB  |  564 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        Accessor.c
  3.  
  4.     Contains:    Object Accessors
  5.  
  6.     Written by:    Francis Stanbach, Greg Anderson
  7.  
  8.     Copyright:    © 1992, 1994-1995 by Apple Computer, Inc., all rights reserved.
  9.  
  10.         <17>      2/8/95    ga
  11. */
  12.  
  13. #include <AERegistry.h>
  14. #include <AppleEvents.h>
  15. #include <AEObjects.h>
  16. #include <AEPackObject.h>
  17. #include <AERegistry.h>
  18.  
  19. #include "Exceptions.h"
  20. #include "MoreAEM.h"
  21. #include "Accessor.h"
  22. #include "MarkToken.h"
  23.  
  24.  
  25.  
  26. #include <Threads.h>
  27.  
  28.  
  29. #if GENERATINGCFM
  30.  
  31. #include <MixedMode.h>
  32.  
  33.     //
  34.     // Make routine descriptors for our callbacks
  35.     //
  36.     static RoutineDescriptor gCompareCallbackRD            = BUILD_ROUTINE_DESCRIPTOR(uppOSLCompareProcInfo,        TAccessor::CompareTokens);
  37.     static RoutineDescriptor gCountCallbackRD            = BUILD_ROUTINE_DESCRIPTOR(uppOSLCountProcInfo,            TAccessor::CountElements);
  38.     static RoutineDescriptor gDisposeCallbackRD            = BUILD_ROUTINE_DESCRIPTOR(uppOSLDisposeTokenProcInfo,    TAccessor::DisposeToken);
  39.     static RoutineDescriptor gCreateMarkCallbackRD        = BUILD_ROUTINE_DESCRIPTOR(uppOSLGetMarkTokenProcInfo,    TAccessor::CreateMark);
  40.     static RoutineDescriptor gAddToMarkCallbackRD        = BUILD_ROUTINE_DESCRIPTOR(uppOSLMarkProcInfo,            TAccessor::AddToMark);
  41.     static RoutineDescriptor gAdjustMarksCallbackRD        = BUILD_ROUTINE_DESCRIPTOR(uppOSLAdjustMarksProcInfo,    TAccessor::AdjustMarks);
  42. //    static RoutineDescriptor gGetErrorDescCallbackRD    = BUILD_ROUTINE_DESCRIPTOR(uppOSLGetErrDescProcInfo,    TAccessor::GetErrorDesc);
  43.     static RoutineDescriptor gNullAccessorRD            = BUILD_ROUTINE_DESCRIPTOR(uppOSLAccessorProcInfo,        TAccessor::NullAccessor);
  44.     static RoutineDescriptor gListAccessorRD            = BUILD_ROUTINE_DESCRIPTOR(uppOSLAccessorProcInfo,        TAccessor::ListAccessor);
  45. //    static RoutineDescriptor gAliasTokenAccessorRD        = BUILD_ROUTINE_DESCRIPTOR(uppOSLAccessorProcInfo,        TAccessor::AliasTokenAccessor);
  46.     static RoutineDescriptor gWildCardAccessorRD        = BUILD_ROUTINE_DESCRIPTOR(uppOSLAccessorProcInfo,        TAccessor::WildCardAccessor);
  47.  
  48. #endif
  49.  
  50.  
  51. #pragma segment Access
  52.  
  53.  
  54. TTokenDescriptor gNullContainer;
  55.  
  56. //----------------------------------------------------------------------------------------
  57. // TAccessor::InstallAEHandlers: 
  58. //
  59. // This method installs all of the callbacks required to support calls to AEResolve.
  60. //----------------------------------------------------------------------------------------
  61. #pragma segment Init
  62. void TAccessor::InstallAEHandlers()
  63.     {
  64.     //
  65.     // Clear out the global error descriptor, just for good measure
  66.     //
  67. //    gErrorDescriptor.ClearDescriptor();
  68.     gNullContainer.ClearDescriptor();
  69.  
  70.  
  71. #if GENERATINGCFM
  72.     //
  73.     // Install routine descriptors for all of our callbacks
  74.     //
  75.     FailErr(AESetObjectCallbacks(    &gCompareCallbackRD,
  76.                                     &gCountCallbackRD,
  77.                                     &gDisposeCallbackRD,
  78.                                     &gCreateMarkCallbackRD,
  79.                                     &gAddToMarkCallbackRD,
  80.                                     &gAdjustMarksCallbackRD,
  81.                                     nil // &gGetErrorDescCallbackRD
  82.                                 ));
  83.     
  84.     //
  85.     // Install routine descriptors for our object accessors
  86.     //
  87.     FailErr(AEInstallObjectAccessor(typeWildCard, typeNull,        &gNullAccessorRD, 0, false));
  88.     FailErr(AEInstallObjectAccessor(typeWildCard, typeAEList,    &gListAccessorRD, 0, false));
  89. //    FailErr(AEInstallObjectAccessor(typeWildCard, 'alis',        &gAliasTokenAccessorRD, 0, false));
  90.     FailErr(AEInstallObjectAccessor(typeWildCard, typeWildCard,    &gWildCardAccessorRD, 0, false));
  91.  
  92. #else
  93.     //
  94.     // Install function pointers to all of our callbacks
  95.     //
  96.     FailErr(AESetObjectCallbacks(    (OSLCompareProcPtr)            &TAccessor::CompareTokens,
  97.                                     (OSLCountProcPtr)            &TAccessor::CountElements,
  98.                                     (OSLDisposeTokenProcPtr)    &TAccessor::DisposeToken,
  99.                                     (OSLGetMarkTokenProcPtr)    &TAccessor::CreateMark,
  100.                                     (OSLMarkProcPtr)            &TAccessor::AddToMark,
  101.                                     (OSLAdjustMarksProcPtr)        &TAccessor::AdjustMarks,
  102.                                     nil // gGetErrorDescCallback
  103.                                 ));
  104.  
  105.     //
  106.     // Install function pointers to our object accessors
  107.     //
  108.     FailErr(AEInstallObjectAccessor(typeWildCard, typeNull,        (OSLAccessorProcPtr)        &TAccessor::NullAccessor, 0, false));
  109.     FailErr(AEInstallObjectAccessor(typeWildCard, typeAEList,    (OSLAccessorProcPtr)        &TAccessor::ListAccessor, 0, false));
  110. //    FailErr(AEInstallObjectAccessor(typeWildCard, 'alis',        (OSLAccessorProcPtr)        &TAccessor::AliasTokenAccessor, 0, false));
  111.     FailErr(AEInstallObjectAccessor(typeWildCard, typeWildCard,    (OSLAccessorProcPtr)        &TAccessor::WildCardAccessor, 0, false));
  112. #endif    
  113.  
  114.  
  115.     //
  116.     // Declare that we know how to deal with marking and whose clauses
  117.     //
  118.     TDescriptor::SetCallbackFlags(kAEIDoMarking + kAEIDoWhose);
  119.     } // TAccessor::InstallAEHandlers 
  120.  
  121.  
  122. //----------------------------------------------------------------------------------------
  123. // TAccessor::CompareTokens: 
  124. //
  125. // CompareTokens is called to resolve 'whose' clauses.  One parameter of the callback,
  126. // 'theObject', is always a token that the object support library asked us to generate.
  127. // The other parameter of the comparison, descOrObject, could be either a literal
  128. // descriptor (such as a string or an integer) or an object specifier.
  129. //
  130. // Abstract Scriptable Object has a framework for dealing with object specifiers in comparison tokens.
  131. // It will automatically call AEResolve to resolve the 'descOrObject' parameter if
  132. // an object specifier is passed in.  It then tries to compare the best data type
  133. // of 'theObject' with the object from 'descOrObject'; if 'descOrObject' cannot provide
  134. // the best type of 'theObject', then it gets the best type of 'descOrObject' and
  135. // tries to do a coercion.  See TAbstractScriptableObject::Compare
  136. //----------------------------------------------------------------------------------------
  137. pascal OSErr TAccessor::CompareTokens(    DescType comparisonOperator,
  138.                                         TTokenDescriptor& theObject,
  139.                                         TDescriptor& descOrObject,
  140.                                         Boolean& result            )
  141.     {
  142.     OSErr err = noErr;
  143.     
  144.     result = false;
  145.     
  146.     Try
  147.         {
  148.         TAbstractScriptableObject* tokenToCompare = nil;
  149.         DescType objectType = theObject.DescriptorType();
  150.         
  151.         //
  152.         // We usually expect that the object's type will be typeTokenObject
  153.         //
  154.         if((objectType == typeTokenObject) || (objectType == typeTokenInHandle))
  155.             {
  156.             tokenToCompare = theObject.TokenObject();
  157.  
  158.             if(tokenToCompare != nil)
  159.                 {
  160.                 result = tokenToCompare->Compare(TAETransaction(), comparisonOperator, descOrObject);
  161.                 }
  162.             }
  163.         //
  164.         // In strange cases, the 'descOrObject' will be a token
  165.         // but the 'theObject' will be a literal.  Inside Macintosh
  166.         // claims this will never happen, but it LIES.
  167.         //
  168.         else if((descOrObject.DescriptorType() == typeTokenObject) || (descOrObject.DescriptorType() == typeTokenInHandle))
  169.             {
  170.             tokenToCompare = ((TTokenDescriptor*)(&descOrObject))->TokenObject();
  171.  
  172.             if(tokenToCompare != nil)
  173.                 {
  174.                 result = tokenToCompare->Compare(TAETransaction(), ReverseComparisonOperator(comparisonOperator),theObject);
  175.                 }
  176.             }
  177.         //
  178.         // If we don't get a token, assume it's raw data
  179.         //
  180.         else
  181.             {
  182.             result = theObject.Compare(comparisonOperator,descOrObject);
  183.             }
  184.         
  185.         }
  186.     Catch(err)
  187.         {
  188.         }
  189.     
  190.     return err;
  191.     } // TAccessor::CompareTokens 
  192.  
  193.  
  194. //----------------------------------------------------------------------------------------
  195. // TAccessor::CountElements: 
  196. //
  197. // The CountElements callback is called by the object support library when handling
  198. // events such as 'whose' clauses.  There is also a 'count' event which is called by
  199. // scripts that have count commands (e.g. "count desktop each file").  Both callbacks
  200. // do essentially the same thing, but we have to support them both.
  201. //
  202. // Note that 'containerToken' might be an AEList, so we need to iterate the elements
  203. // of the list with the 'ForEachToken' macro.
  204. //----------------------------------------------------------------------------------------
  205. pascal OSErr TAccessor::CountElements(    DescType classToCount,
  206.                                         DescType containerClass,
  207.                                         TTokenDescriptor& containerToken,
  208.                                         long& result            )
  209.     {
  210.     TTokenDescriptor nullContainerDesc;
  211.     long theCount = 0;
  212.     OSErr err = noErr;
  213.  
  214.     Try
  215.         {
  216.         //
  217.         // If the containerToken is a null descriptor, then create
  218.         // a token for the null container and count the elements of it
  219.         //
  220.         if(containerToken.IsNullDescriptor())
  221.             {
  222.             nullContainerDesc = CreateNullContainerToken();
  223.             FailErr( TAccessor::CountElements(classToCount, containerClass, nullContainerDesc, theCount) );
  224.             nullContainerDesc.DisposeToken();
  225.             }
  226.         //
  227.         // Count the elements of each token in the containerToken
  228.         //
  229.         else
  230.             {
  231.             theCount = containerToken.TokenObject()->CountElements(TAETransaction(), classToCount);
  232.             }
  233.         }
  234.     Catch(err)
  235.         {
  236.         nullContainerDesc.DisposeToken();
  237.         }
  238.     
  239.     result = theCount;
  240.     
  241.     return err;
  242.     } // TAccessor::CountElements
  243.  
  244. //----------------------------------------------------------------------------------------
  245. // TAccessor::DisposeToken: 
  246. //
  247. // The dispose token method is called every time the object support library or our
  248. // code calls AEDisposeToken.  As a general rule, our dispose token callback will
  249. // never be passed a descriptor that was not generated by one of our accessor functions.
  250. // The exception to this rule is that the object support library will place our tokens
  251. // inside of AELists if it is processing a 'whose' clause, or resolving some other
  252. // object specifier that specifies multiple tokens (formRange, for example).  If it
  253. // does so, it will pass the AEList to our dispose token callback; we then have to pull
  254. // all of the tokens out of it just to immediately delete them again.  If we do not do
  255. // this, then tokens that own other objects in memory would never have a chance to clean
  256. // up the memory they use, and we would have a nasty memory leak.
  257. //
  258. // The 'marking' mechanism of the object support library could be used to avoid copying
  259. // our tokens into AELists, but we would have to take on the responsibility of dealing
  260. // with maintaining collections of tokens if we used marking.
  261. //----------------------------------------------------------------------------------------
  262. pascal OSErr TAccessor::DisposeToken(TTokenDescriptor& tokenDesc)
  263.     {
  264.     OSErr err = noErr;
  265.     
  266.     Try
  267.         {
  268.         DescType typeToDelete = tokenDesc.DescriptorType();
  269.         
  270.         //
  271.         // Is this token derived from TTokenObject?
  272.         //
  273.         if((typeToDelete == typeTokenObject) || (typeToDelete == typeTokenInHandle))
  274.             {
  275.             TAbstractScriptableObject* token = tokenDesc.TokenObject();
  276.             if(token != nil)
  277.                 {
  278.                 token->DisposeDesignator();
  279.                 }
  280.             
  281.             if(typeToDelete == typeTokenObject)
  282.                 tokenDesc.ClearDescriptor();
  283.             else
  284.                 tokenDesc.Dispose();
  285.             }
  286.         //
  287.         // Is this token an AEList?
  288.         //
  289.         else if(typeToDelete == typeAEList)
  290.             {
  291.             //
  292.             // Dispose of each token inside the list in turn
  293.             //
  294.             //FOREACHTOKEN(&tokenDesc, tokenDescriptor)
  295.             //    {
  296.             //    tokenDescriptor.DisposeToken();
  297.             //    }
  298.             //
  299.             // Once we get rid of the contents of the list,
  300.             // we must delete the list itself as well
  301.             //
  302.             //tokenDesc.Dispose();
  303.             }
  304.         //
  305.         // Disposing a null descriptor?  Do nothing
  306.         //
  307.         // If we don't recognize the token, then fail with errAEEventNotHandled.
  308.         // The object support library will then try to dispose of it for us
  309.         //
  310.         else if(typeToDelete != typeNull)
  311.             {
  312.             FailErr(errAEEventNotHandled);
  313.             }
  314.         }
  315.     Catch(err)
  316.         {
  317.         }
  318.     
  319.     return err;
  320.     } // TAccessor::DisposeToken 
  321.  
  322. //----------------------------------------------------------------------------------------
  323. // TAccessor::CreateMark: 
  324. //----------------------------------------------------------------------------------------
  325. pascal OSErr TAccessor::CreateMark(    TTokenDescriptor& /*containerToken*/,
  326.                                     DescType /*desiredClass*/,
  327.                                     TTokenDescriptor* markTokenDesc )
  328.     {
  329.     TMarkToken* markToken;
  330.     OSErr theErr = noErr;
  331.     
  332.     markTokenDesc->ClearDescriptor();
  333.     NOREGISTER(markToken);
  334.  
  335.     Try
  336.         {
  337.         markToken = new TMarkToken(kAlwaysMakeCollection);
  338.         markToken->IMarkToken();
  339.  
  340.         markTokenDesc->AdoptToken(markToken);
  341.         }
  342.     Catch(theErr)
  343.         {
  344.         if(markToken != nil)
  345.             delete markToken;
  346.         }
  347.     
  348.     return theErr;
  349.     } // TAccessor::CreateMark 
  350.     
  351. //----------------------------------------------------------------------------------------
  352. // TAccessor::AddToMark: 
  353. //----------------------------------------------------------------------------------------
  354. pascal OSErr TAccessor::AddToMark(    TTokenDescriptor& tokenToAdd,
  355.                                     TTokenDescriptor& markToken,
  356.                                     long /*markCount*/ )
  357.     {
  358.     OSErr err = noErr;
  359.     
  360.     Try
  361.         {
  362.         markToken.TokenObject()->AdoptToken(tokenToAdd.TokenObject()->CloneDesignator(), kAlwaysMakeCollection);
  363.         }
  364.     Catch(err)
  365.         {
  366.         }
  367.     
  368.     return err;
  369.     } // TAccessor::AddToMark 
  370.     
  371. //----------------------------------------------------------------------------------------
  372. // TAccessor::AdjustMarks: 
  373. //----------------------------------------------------------------------------------------
  374. pascal OSErr TAccessor::AdjustMarks(long newStart,
  375.                                     long newStop,
  376.                                     TTokenDescriptor& markTokenDesc )
  377.     {    
  378.     //
  379.     // It's fairly reasonable to downcast to TMarkToken here, because
  380.     // we trust the object support library to only give us mark tokens
  381.     // that we generated with TAccessor::CreateMark.  Still, we'll
  382.     // do a membership test so as to not be completely evil.
  383.     //
  384.     TMarkToken* markToken = (TMarkToken*)markTokenDesc.TokenObject();
  385.     if(markToken->DerivedFromOSLClass(TAETransaction(),cMarkToken))
  386.         markToken->AdjustMarks(newStart, newStop);
  387.     
  388.     return noErr;
  389.     } // TAccessor::AdjustMarks 
  390.  
  391. //----------------------------------------------------------------------------------------
  392. // TAccessor::NullAccessor:
  393. //
  394. // The null accessor is called whenever the containerToken is null; its only function
  395. // is to fill in the global container (the desktop) and call the wildcard accessor.
  396. // We could have just made the WildCard accessor recognize the null container, but
  397. // I preferred to do it this way since it simplified the creation and deletion of the
  398. // temporary null container token.
  399. //
  400. // A new null container token is created and disposed every time the null accessor
  401. // is called just to be consistant with our memory ownership rules and lifetime/scope
  402. // of tokens rules which involve always creating tokens when they are needed and
  403. // disposing them again when their life is over.  I could have also made the dispose
  404. // token routine check for the null container token and not delete it; that would have
  405. // allowed me to create but a single null container token which could be reused each
  406. // time the null accessor was called.  I opted to aviod the special checking, but there
  407. // is no reason why the code couldn't be optimized to do it.
  408. //----------------------------------------------------------------------------------------
  409. pascal OSErr TAccessor::NullAccessor(    DescType desiredClass,
  410.                                         TTokenDescriptor& /*containerToken*/,
  411.                                         DescType containerClass,
  412.                                         DescType keyForm,
  413.                                         TDescriptor& keyData,
  414.                                         TTokenDescriptor* token,
  415.                                         long hRefCon )
  416.     {
  417.  
  418.     OSErr err = noErr;
  419.     
  420.     Try
  421.         {
  422.         if(gNullContainer.IsNullDescriptor())
  423.             gNullContainer = CreateNullContainerToken();
  424.         
  425.         FailErr( WildCardAccessor(desiredClass, gNullContainer, containerClass,
  426.                     keyForm, keyData, token, hRefCon) );
  427.         }
  428.     Catch(err)
  429.         {
  430.         }
  431.     
  432.     return err;
  433.     } // TAccessor::NullAccessor
  434.  
  435.  
  436. //----------------------------------------------------------------------------------------
  437. // TAccessor::ListAccessor: 
  438. //
  439. // Once again, the object support library might pass an AEList in as the
  440. // 'containerToken', so we iterate over each item in the list with the 'ForEachToken'
  441. // macro.  If our accessor returns multiple objects (or if we call multiple accessors),
  442. // then we will also put the resulting tokens into an AEList and return it to the
  443. // object support library.  Note that this accessor is never called if your application
  444. // supports marking.
  445. //
  446. // It begs the question:  why doesn't the object support library handle this case
  447. // for us, huh?
  448. //----------------------------------------------------------------------------------------
  449. pascal OSErr TAccessor::ListAccessor(        DescType desiredClass,
  450.                                             TTokenDescriptor& containerToken,
  451.                                             DescType containerClass,
  452.                                             DescType keyForm,
  453.                                             TDescriptor& keyData,
  454.                                             TTokenDescriptor* resultToken,
  455.                                             long /*hRefCon*/ )
  456.     {
  457.     TTokenDescriptor tokenDescriptor;
  458.     OSErr err = noErr;
  459.     resultToken->ClearDescriptor();
  460.  
  461.     //
  462.     // If the containerToken is an empty list, we could fail
  463.     // with the result 'errAEEmptyListContainer'.
  464.     //
  465.     
  466.     Try
  467.         {
  468.         for(TDescriptorIterator iter(containerToken); iter.More(); iter.Next())
  469.             {
  470.             AECallObjectAccessor(desiredClass, iter.Current(), containerClass,
  471.                 keyForm, keyData, (AEDesc*) &tokenDescriptor);
  472.             
  473.             resultToken->AdoptToken(tokenDescriptor);
  474.             tokenDescriptor.ClearDescriptor();
  475.             }
  476.         }
  477.     Catch(err)
  478.         {
  479.         }
  480.         
  481.     //
  482.     // It's a bit gross to do this, but currently,
  483.     // access by name does not fail if no item with
  484.     // the given name can be found.  We must set
  485.     // the error code here
  486.     //
  487.     if((err == noErr) && (resultToken->IsNullDescriptor()))
  488.         {
  489.         err = errAENoSuchObject;
  490.         }
  491.     
  492.     return err;
  493.     } // TAccessor::ListAccessor 
  494.  
  495.  
  496. //----------------------------------------------------------------------------------------
  497. // TAccessor::WildCardAccessor: 
  498. //
  499. // The wildcard accessor does nothing more than pass the access message to the
  500. // container that it was passed.  TTokenObject::Access then calls through to the
  501. // appropriate method (AccessByIndex, AccessByName, or AccessByProperty) of the
  502. // container.
  503. //----------------------------------------------------------------------------------------
  504. pascal OSErr TAccessor::WildCardAccessor(    DescType desiredClass,
  505.                                             TTokenDescriptor& containerToken,
  506.                                             DescType /*containerClass*/,
  507.                                             DescType keyForm,
  508.                                             TDescriptor& keyData,
  509.                                             TTokenDescriptor* resultToken,
  510.                                             long /*hRefCon*/ )
  511.     {
  512.     OSErr err = noErr;
  513.     resultToken->ClearDescriptor();
  514.     
  515.     Try
  516.         {
  517.         TAbstractScriptableObject* result = nil;
  518.         
  519.         result = containerToken.TokenObject()->Access(TAETransaction(),desiredClass,keyForm,keyData);
  520.         resultToken->AdoptToken(result);
  521.         }
  522.     Catch(err)
  523.         {        
  524.         //
  525.         // If the keyForm is not recognized, then the error that should be returned is
  526.         // errAEBadKeyForm.  However, if the keyForm is formWhose, and the whose clause
  527.         // is not handled by the token that it was sent to, then the Object Support
  528.         // Library expects the error code to be errAEEventNotHandled.  We special-case
  529.         // this condition right here, but we could also do it in TAbstractScriptableObject::Access.
  530.         //
  531.         if((err == errAEBadKeyForm) && (keyForm == formWhose))
  532.             {
  533.             err = errAEEventNotHandled;
  534.             }
  535.         
  536.         }
  537.     
  538.     //
  539.     // It's a bit gross to do this, but currently,
  540.     // access by name does not fail if no item with
  541.     // the given name can be found.  We must set
  542.     // the error code here.
  543.     //
  544.     // ••• Question:  can we insure that Access will
  545.     // always fail if nothing is returned, and skag this code?
  546.     //
  547.     if((err == noErr) && (resultToken->IsNullDescriptor()))
  548.         {
  549.         ASSERTPRINT(false, ("WildCardAccessor Result is typeNull but error not set"));
  550.         err = errAENoSuchObject;
  551.         }
  552.     
  553.  
  554.     //
  555.     // Give other threads time before we actually return to OSL
  556.     //
  557.     YieldToAnyThread();
  558.  
  559.  
  560.     return err;
  561.     } // TAccessor::WildCardAccessor 
  562.  
  563.  
  564.